package top.codedance.iotp.client.plugins.sync.udp;

import cd.io.netty.bootstrap.Bootstrap;
import cd.io.netty.buffer.Unpooled;
import cd.io.netty.channel.*;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.DatagramPacket;
import cd.io.netty.channel.socket.nio.NioDatagramChannel;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.client.plugins.sync.udp.bean.UDPParam;
import top.codedance.iotp.client.plugins.sync.udp.listenner.UdpInitializer;
import org.apache.log4j.Logger;
import org.ini4j.Ini;
import org.ini4j.Wini;

import java.net.InetSocketAddress;

public class UdpClient implements Runnable {
    private Logger logger = ClientApplication.getLogger(UdpClient.class);
    private UdpExecuterImpl udpExecuter = null;
    private UDPParam udpParam = null;
    private Integer clientId = 0;
    private ChannelFuture channelFuture = null;
    private Wini ini = null;

    public UdpClient(UdpExecuterImpl udpExecuter, Integer clientId, Wini ini){
        this.udpExecuter = udpExecuter;
        this.clientId = clientId;
        this.ini = ini;
    }

    @Override
    public void run() {
        initConfig();
        if(udpParam != null){
            connect();
        }else{
            logger.debug("UDP config fail.");
            UdpExecuterImpl.clients.remove(clientId);
        }
    }

    private void initConfig(){
        String ip = null;
        int port = 34567;
        if (ini != null && ini.containsKey("udp")) {
            Ini.Section section = ini.get("udp");
            if (section.containsKey("ip." + clientId)) {
                ip = section.get("ip." + clientId);
            }
            if (section.containsKey("port." + clientId)) {
                port = Integer.parseInt(section.get("port." + clientId));
            }
            udpParam = new UDPParam(ip, port);
        }else{
            udpParam = null;
            logger.info("udp settings is fail.");
        }
    }

    private void connect(){
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new UdpInitializer(this));
            channelFuture = bootstrap.bind(0).sync();
            UdpExecuterImpl.clients.put(clientId, this);
            channelFuture.channel().closeFuture().await();
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
    }

    public void send(byte[] data){
        try {
            if(channelFuture != null && channelFuture.channel().isOpen() && channelFuture.channel().isActive()) {
                channelFuture.channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(data), new InetSocketAddress(udpParam.getIp(), udpParam.getPort())));
            }
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    public void close(){
        try {
            channelFuture.channel().closeFuture().await();
            UdpExecuterImpl.clients.remove(clientId);
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    /**
     * 添加数据到调用者
     * @param data
     */
    public void appendResult(byte[] data){
        this.udpExecuter.appendResult(data);
    }
}
